<template>
  <div class="gz-editor">
    <diagram v-if="show"
             ref="diag"
             :bds-list="bdsList"
             :model-data="modelData"
             :style="{height: height}"
             @model-changed="modelChanged"
             @add-item="handleAddBtnClick"
             @item-dbclick="handleEditBtnClick"
             @item-contextmenu="handleContextmenu"
             @changed-selection="changedSelection"/>

    <xj-dialog :visible="editBdsDialogVisible" title="编辑表达式">
      <div class="edit-row">
        <el-link
            v-for="(value,key) in bdsNameObj"
            :key="key" type="primary" @click="insertToBds(`calc('`+key+`')`)">{{ key }}
        </el-link>
      </div>
      <div class="edit-row">
        <el-link
            v-for="(name,ysf) in ysfObj"
            :key="ysf" type="primary" @click="insertToBds(ysf)">{{ name }}
        </el-link>
      </div>
      <el-form :model="bdsForm" label-width="85px">
        <el-form-item label="表达式">
          <bds-input-box ref="bdsInput" v-model="bdsForm.bds"></bds-input-box>
        </el-form-item>
        <el-form-item label="运算类型">
          <el-radio-group v-model="bdsForm.yslx">
            <el-radio-button :label="null">普通运算</el-radio-button>
            <el-radio-button label="BLYS">遍历运算</el-radio-button>
          </el-radio-group>
          <!--          <el-select v-model="bdsForm.yslx" style="width: 100%;">-->
          <!--            <el-option :value="null" label="普通运算"></el-option>-->
          <!--            <el-option label="遍历运算" value="BLYS"></el-option>-->
          <!--          </el-select>-->
        </el-form-item>
        <el-form-item label="实体名称">
          <el-select v-model="bdsForm.bm"
                     :class="{red:isRed}"
                     style="width: 100%;"
                     value-key="bmc">
            <el-option
                v-for="bmObj in bmList"
                :key="bmObj.bmc"
                :label="bmObj.bzwmc"
                :value="bmObj">
              <div :style="{
                color :bmObj.first?'':'red'
              }">{{ bmObj.bzwmc }}
              </div>
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="">
          <div style="color:red;">遍历运算：遍历运算时可能需要选择【多】的实体</div>
          <div style="color:red;">红色表名：非表达式中的实体名称</div>
        </el-form-item>

      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="handleConfirmBtnClick(false)">取消</el-button>
        <el-button type="primary" @click="handleConfirmBtnClick(true)">确定</el-button>
      </div>
    </xj-dialog>
  </div>
</template>

<script>
import uuid from 'uuid/v4'
import XjDialog from "@/components/XjDialog";
import BdsInputBox from "@/components/BdsInputBox";

import Diagram from '@/components/Diagram.vue'

export default {
  name: "GzEditor",
  components: { XjDialog, BdsInputBox, Diagram },
  props: {
    gzbds: {
      required: true
    },
    bdsList: {
      type: Array,
      default: () => ([]),
      required: true,
    },
    bdsNameObj: {
      type: Object,
      default: () => ({}),
      required: true,
    }
  },
  data () {
    return {
      modelData: {
        linkFromPortIdProperty: "fromport",
        nodeDataArray: [
          {
            "key": "562db998-d6bc-4a2b-9bcf-0db5698b858b",
            "data": { "bds": "calc('bds1')", "yslx": null, "bmc": null, "yes": [], "no": [] },
            "category": "decision",
          }
        ],
        linkDataArray: [],
      },
      show: true,
      height: '50vh',
      editBdsDialogVisible: false,
      myDiagram: null,
      ysfObj: {
        "&&": '并且',
        "||": '或者',
        ">": '大于',
        ">=": '大于等于',
        "<": '小于',
        "<=": '小于等于',
        "!=": '不等于',
        "==": '等于',
      },
      bdsForm: {
        bm: { bmc: '' }
      },
      node: null
    }
  },
  computed: {
    isRed () {
      let bm = this.bdsForm.bm
      if (!bm) return false
      let obj = this.bmList.find(item => item.bmc === bm.bmc)
      return obj ? !obj.first : false
    },
    bmList () {
      let arr = []
      let bdsmcList = getBdsmcList(this.bdsForm.bds)
      for (let resKey of bdsmcList) {
        let bdsNameObjElement = this.bdsNameObj[resKey];
        if (!bdsNameObjElement) continue
        bdsNameObjElement.forEach(item => {
          let bmc = item.zdmc.split(".")[0]
          let b = arr.find(_item => _item.bmc === bmc)
          if (!b) {
            arr.push({
              bmc,
              first: true,
              bzwmc: item.zdzwmc.split(".")[0]
            })
          }
        })
      }
      for (let resKey in this.bdsNameObj) {
        let bdsNameObjElement = this.bdsNameObj[resKey];
        if (!bdsNameObjElement) continue
        bdsNameObjElement.forEach(item => {
          let bmc = item.zdmc.split(".")[0]
          let b = arr.find(_item => _item.bmc === bmc)
          if (!b) {
            arr.push({
              bmc,
              first: false,
              bzwmc: item.zdzwmc.split(".")[0]
            })
          }
        })
      }
      return arr
    }
  },
  watch: {
    'bdsForm.bds' () {
      if (!this.bdsForm.bm.bmc) {
        let bmObj = this.bmList[0]
        if (bmObj) this.bdsForm.bm.bmc = bmObj.bmc
      }
    },
    gzbds: {
      handler () {
        this.gzbdsToModelData(this.gzbds)
      },
      deep: true
    }
  },
  mounted () {
    this.gzbdsToModelData(this.gzbds)
  },
  methods: {
    getModel (arr, pnode, fromport) {
      let resobj = { nodeDataArray: [], linkDataArray: [] };
      for (let obj of arr) {
        let node = this.getNode(obj)
        resobj.nodeDataArray.push(node);
        if (pnode) {
          let link = this.getLink(node, pnode, fromport)
          resobj.linkDataArray.push(link)
        }
        if (!obj) continue
        if (obj.yes) {
          let nodes = this.getModel(obj.yes, node, "yes")
          resobj.nodeDataArray.push(...nodes.nodeDataArray)
          resobj.linkDataArray.push(...nodes.linkDataArray)
        }
        obj.yes = []
        if (obj.no) {
          let nodes = this.getModel(obj.no, node, "no")
          resobj.nodeDataArray.push(...nodes.nodeDataArray)
          resobj.linkDataArray.push(...nodes.linkDataArray)
        }
        obj.no = []
      }
      return resobj
    },
    getNode (obj) {
      obj = obj || {
        bds: "",
        bmc: "",
        no: [],
        yes: [],
        yslx: null
      }
      return {
        key: uuid(),
        data: obj,
        category: "decision"
      }
    },
    getLink (node, pnode, fromport) {
      return {
        category: 'auditedLineColor',
        from: pnode.key,
        fromport,
        to: node.key
      }
    },
    gzbdsToModelData (gzbds) {
      gzbds = JSON.parse(JSON.stringify(gzbds))
      let val = this.getModel([gzbds])
      for (var p in val) {
        this.modelData[p] = val[p];
      }
    },
    modelChanged (e) {
    },

    changedSelection (e) {
    },

    reset () {
      this.gzbdsToModelData(this.gzbds)
    },
    insertToBds (str) {
      // let bds = this.bdsForm.bds
      // if (bds === null) bds = ""
      // this.bdsForm.bds = (bds + ` ${str}`).trim()
      let bdsInput = this.$refs.bdsInput
      bdsInput.insert(str)
    },
    getGzbds (ignore) {
      let model = this.modelData
      let nodeDataArray = JSON.parse(JSON.stringify(model.nodeDataArray))
      let hasNoBds = nodeDataArray.some(item => !item.data.bds)
      if (hasNoBds && !ignore) throw new Error("规则尚未编辑完成")
      let linkDataArray = JSON.parse(JSON.stringify(model.linkDataArray))
      for (let link of linkDataArray) {
        let from = nodeDataArray.find(item => item.key === link.from)
        if (!from.data.bds && !ignore) {
          return this.$message({ message: "存在空白节点", sjly: "error" })
        }
        let to = nodeDataArray.find(item => item.key === link.to)
        if (!to.data.bds && !ignore) {
          return this.$message({ message: "存在空白节点", sjly: "error" })
        }
        from.data[link.fromport].push(to.data)
        to.isChild = true
      }
      return nodeDataArray.find(item => !item.isChild).data
    },
    model () {
      return this.$refs.diag.model();
    },

    updateDiagramFromData () {
      this.$refs.diag.updateDiagramFromData();
    },
    handleConfirmBtnClick (isConfirm) {
      if (isConfirm) {
        if (this.bdsForm.bm) {
          this.bdsForm.bmc = this.bdsForm.bm.bmc
        }
        this.node.data = JSON.parse(JSON.stringify(this.bdsForm))
        this.model().updateTargetBindings(this.node)
      }
      this.editBdsDialogVisible = false
      this.bdsForm = { bm: {} }
    },
    async fullScreen (b) {
      let gzbds = this.getGzbds(true);
      this.height = b ? `calc(100vh - 64px - 70px)` : '50vh'
      this.show = false
      await this.$nextTick()
      this.show = true
      await this.$nextTick()
      this.gzbdsToModelData(gzbds)
    },
    objIsNull (obj) {
      for (const bdsmc in obj) {
        return false
      }
      return true
    },
    handleEditBtnClick (e, node) {
      if (this.objIsNull(this.bdsNameObj)) {
        return this.$message({ message: '请先添加表达式', sjly: 'error' })
      }
      let data = node.part.data
      this.bdsForm = {
        ...data.data,
        bm: {
          bmc: data.data.bmc
        }
      }
      this.node = data
      this.editBdsDialogVisible = true
    },
    handleContextmenu (e, node) {
      // let node = e.diagram.selection.first()

      const getFromport = async () => {
        let fromport = 'no'
        try {
          await this.$confirm('请选择接线位置<br><span style="color:orange">右上角取消插入</span>', {
            cancelButtonText: " ",
            confirmButtonText: " ",
            confirmButtonClass: 'trueBtn',
            cancelButtonClass: 'falseBtn',
            distinguishCancelAndClose: true,
            dangerouslyUseHTMLString: true
          })
          fromport = 'yes'
        } catch (e) {
          if (e === 'close') return
          fromport = 'no'
        }
        return fromport
      }

      let data = node.part.data
      const { nodeDataArray, linkDataArray } = this.modelData
      const fromLinkList = linkDataArray.filter(item => item.to === data.key)
      const toLinkList = linkDataArray.filter(item => item.from === data.key)
      const hasYes = toLinkList.some(item => item.fromport === 'yes')
      const hasNo = toLinkList.some(item => item.fromport === 'no')
      this.$contextmenu([{
        label: "前面插入节点",
        icon: require("@/assets/svg/insertBef.svg"),
        show: true,
        click: async () => {
          let fromport = await getFromport()
          if (!fromport) return

          let oldLink = linkDataArray.find(item => item.to === data.key)

          if (oldLink) {
            oldLink.from = this.addNodeAndLink(oldLink.from, oldLink.fromport)
            oldLink.fromport = fromport
          } else {
            const newKey = this.addNodeAndLink()
            linkDataArray.push({
              category: "auditedLineColor",
              fromport: fromport,
              from: newKey,
              to: data.key
            })
          }
        }
      }, {
        label: "后面插入节点",
        icon: require("@/assets/svg/insertAft.svg"),
        show: toLinkList.length > 1,
        click: async () => {
          let fromport = await getFromport()
          if (!fromport) return

          let oldLinkList = linkDataArray.filter(item => item.from === data.key)
          let newKey = this.addNodeAndLink(data.key, fromport)
          for (let oldLinkListElement of oldLinkList) {
            oldLinkListElement.from = newKey
          }
        }
      }, {
        label: '改变入线位置',
        icon: require("@/assets/svg/gbrxwz.svg"),
        show: fromLinkList.length,
        click: async () => {
          let fromport = await getFromport()
          if (!fromport) return
          let oldLink = linkDataArray.find(item => item.to === data.key)
          oldLink.fromport = fromport
        }
      }, {
        label: "删除节点",
        icon: require("@/assets/svg/delete.svg"),
        show: fromLinkList.length || (!fromLinkList.length && toLinkList.length === 1),
        click: async () => {
          this.deleteNode(data)
        }
      }, {
        label: "删除之后节点",
        icon: require("@/assets/svg/delete.svg"),
        show: toLinkList.length,
        click: async () => {

          const deleteNodes = () => {
            let linkList = linkDataArray.filter(item => item.from === data.key)
            if (linkList) {
              for (let linkListElement of linkList) {
                let node = nodeDataArray.find(item => item.key === linkListElement.to)
                if (node) {
                  this.deleteNode(node)
                  deleteNodes()
                }
              }
            }
          }
          deleteNodes()
        }
      }])
    },
    deleteNode (data) {
      const { nodeDataArray, linkDataArray } = this.modelData

      // if (data.data.bds) {
      //   try {
      //     await this.$confirm('删除后不可恢复，是否删除？', {
      //       confirmButtonClass: 'deleteBtn'
      //     })
      //   } catch (e) {
      //     return
      //   }
      // }
      let idx = nodeDataArray.findIndex(item => item.key === data.key)
      nodeDataArray.splice(idx, 1)
      let fromObj = linkDataArray.find(item => item.to === data.key)
      let toList = linkDataArray.filter(item => item.from === data.key)
      for (let toListElement of toList) {
        if (fromObj) {
          toListElement.from = fromObj.from
          toListElement.fromport = fromObj.fromport
        }
      }
      for (let i = 0; i < linkDataArray.length; i++) {
        let link = linkDataArray[i]
        if (!nodeDataArray.find(item => item.key === link.to) || !nodeDataArray.find(item => item.key === link.from)) {
          linkDataArray.splice(i, 1)
          i--
        }
      }
      linkDataArray.sort((a, b) => {
        let aIdx = nodeDataArray.findIndex(item => item.key === a.to)
        let bIdx = nodeDataArray.findIndex(item => item.key === b.to)
        return aIdx - bIdx
      })
    },
    handleAddBtnClick (e, node) {
      let data = node.part.data
      if (!data.data.bds) return this.$message.warning('请先完善此节点')
      this.addNodeAndLink(data.key, node.portId)
    },
    addNodeAndLink (prevKey, fromport) {
      const { nodeDataArray, linkDataArray } = this.modelData

      let newKey = uuid()
      let idx = nodeDataArray.findIndex(item => item.key === prevKey)
      nodeDataArray.splice(idx + 1, 0, {
        key: newKey,
        data: { bds: "", yslx: null, bmc: "", yes: [], no: [] },
        category: "decision",
      })
      if (!prevKey) return newKey
      let num = linkDataArray.length - 1
      if (fromport !== 'no') num = -1

      for (let i = linkDataArray.length - 1; i >= 0; i--) {
        let _link = linkDataArray[i]
        if (_link.from === prevKey && _link.fromport === fromport) {
          num = i;
          break
        }
      }
      linkDataArray.splice(num + 1, 0, {
        category: "auditedLineColor",
        fromport: fromport,
        from: prevKey,
        to: newKey
      })
      return newKey
    }
  }
}
</script>

<style lang="scss" scoped>
.gz-editor {
  .edit-row {
    text-align: center;
    margin: 5px 0;

    a {
      margin: 0 5px;
    }
  }

  ::v-deep .el-select.red {
    .el-input {
      .el-input__inner {
        color: red;
      }
    }
  }
}
</style>
